package com.shenchuangit.rydemo.util;

import com.shenchuangit.rydemo.util.xirr.Transaction;
import com.shenchuangit.rydemo.util.xirr.Xirr;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.List;

public class QuotedUtils {

    /*
    ●Rate贷款利率（期利率）。
    ●Nper该项贷款的付款总期数(总年数或还租期数）。
    ● Pv现值（租赁本金），或一系列未来付款的当前值的累积和，也称为本金。
    ●Fv为未来值（余值），或在最后一次付款后希望得到的现金余额，如果省略Fv，则假设其值为零，也就是一笔贷款的未来值为零。
    ●Type数字0或1，用以指定各期的付款时间是在期初还是期末。1代表期初（先付：每期的第一天付），不输入或输入0代表期末（后付：每期的最后一天付）。
    参数说明
    PMT 返回的支付款项包括本金和利息，但不包括税款、保留支付或某些与贷款有关的费用。
    应确认所指定的 rate 和 nper 单位的一致性。例如，同样是四年期年利率为 12% 的贷款，如果按月支付，rate 应为 12%/12，nper 应为 4*12；如果按年支付，rate 应为 12%，nper 为 4。
     */
    public static BigDecimal PMT(BigDecimal Rate, int nper, BigDecimal Pv) {
        MathContext mc= MathContext.DECIMAL128;
        //Excel equivalent formula== P*R*POWER(1+R,N)/(POWER(1+R,N)-1)
        BigDecimal nemi1= Pv.multiply(Rate,mc);
        BigDecimal npower1= (BigDecimal.ONE.add(Rate)).pow(nper,mc);
        BigDecimal dpower1= (BigDecimal.ONE.add(Rate)).pow(nper,mc);
        BigDecimal denominator= dpower1.subtract(BigDecimal.ONE);
        BigDecimal numerator= nemi1.multiply(npower1);
        BigDecimal emi= numerator.divide(denominator,2, BigDecimal.ROUND_HALF_UP);
        return emi;
    }
    /**
     * 默认猜测值
     */
    private static final double X0 = 0.1D;

    /**
     *  计算 XIRR
     *  【参考列表】
     *  IRR Excel:https://support.office.com/zh-cn/article/IRR-%E5%87%BD%E6%95%B0-64925eaa-9988-495b-b290-3ad0c163c1bc
     *  XIRR Excel:https://support.office.com/zh-cn/article/XIRR-%E5%87%BD%E6%95%B0-de1242ec-6477-445b-b11b-a303ad9adc9d
     * @param list 现金流
     * @param days 计算周期
     * @return
     */
    public static Double xirr(List<Transaction> list, double days) {
        return xirr(list,days,X0);
    }

    /**
     * XIRR
     * @param list 现金流
     * @param days 计算周期
     * @param guess 猜测值
     * @return
     */
    public static Double xirr(List<Transaction> list, double days, double guess) {
        return new Xirr(list,days,guess).xirr();
    }

    /**
     * IRR
     * @param list
     * @return
     */
    public static Double irr(List<Transaction> list) {
        return irr(list,X0);
    }

    /**
     * 计算IRR
     * 【参考列表】
     *  IRR Excel:https://support.office.com/zh-cn/article/IRR-%E5%87%BD%E6%95%B0-64925eaa-9988-495b-b290-3ad0c163c1bc
     *  XIRR Excel:https://support.office.com/zh-cn/article/XIRR-%E5%87%BD%E6%95%B0-de1242ec-6477-445b-b11b-a303ad9adc9d
     *  @param list 现金流
     *  @param guess 猜测值
     */
    public static Double irr(List<Transaction> list, double guess) {
        Double irrResult = 0.0D / 0.0D;
        if (!list.isEmpty()) {
           // int maxIterationCount = 20;
            int maxIterationCount = 100000;
            double absoluteAccuracy = 1.0E-7D;
            double fValue, fDerivative, x1;
            for (int i = 0; i < maxIterationCount; ++i) {
                fValue = 0.0D;
                fDerivative = 0.0D;
                for (int k = 0; k < list.size(); ++k) {
                    fValue += list.get(k).getAmount() / Math.pow(1.0D + guess, (double) k);
                    fDerivative += (double) (-k) * list.get(k).getAmount() / Math.pow(1.0D + guess, (double) (k + 1));
                }
                x1 = guess - fValue / fDerivative;
                if (Math.abs(x1 - guess) <= absoluteAccuracy) {
                    irrResult = x1;
                    break;
                }
                guess = x1;
            }
        }
        return irrResult;
    }
}
